﻿using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Globalization;

namespace OpenLS.Spreadsheet.UI
{
    static class AutoFillFunctionBuilder
    {
        public static  Func<int, object> GetFunction(Cell baseCell, Cell lastCell, int length, NavigationDirection direction)
        {
            Contract.Requires(baseCell != null);
            Contract.Requires(lastCell != null);
            object val = baseCell.Value;
            Func<int, object> result;
            if (val is double)
            {
                var firstValue = (double)val;
                var lastValue = (double)lastCell.Value;
                double baseValue = lastValue;
                double increment;
                if (length > 1)
                    increment = (lastValue - firstValue) / (length - 1);
                else
                    increment = 1;
                result = (index => baseValue + index * increment);
            }
            else if (val is DateTime)
            {
                var firstValue = (DateTime)val;
                DateTime lastValue;
                if (lastCell.Value is DateTime)
                    lastValue = (DateTime) lastCell.Value;
                else if (lastCell.Value is double)
                    lastValue = lastCell.Worksheet.Workbook.DoubleToDateTime((double) lastCell.Value);
                else
                {
                    throw new NotSupportedException();
                }
                var baseValue = lastValue;
                if (firstValue.Year == 1899 && firstValue.Month == 12 && firstValue.Day == 31 && firstValue.Minute == 0 && firstValue.Second == 0)
                {
                    double increment = TimeSpan.FromHours(1).TotalSeconds;
                    if (length > 1)
                        increment = (lastValue - firstValue).TotalSeconds / (length - 1);

                    result = (index => baseValue.AddSeconds(increment * index));
                }
                else
                {
                    int increment;
                    if (length > 1)
                        increment = (int) ((lastValue - firstValue).TotalDays / (length - 1));
                    else
                        increment = 1;

                    result = (index => baseValue.AddDays(index * increment));
                }
            }
            else if (val is string)
            {
                result = getAnyFunction(getValuesOf(baseCell, lastCell, direction));
            }
            else if (val == null)
            {
                result = (i => null);
            }
            else
                throw new NotImplementedException();
            return result;
        }
        private static List<string> getValuesOf(Cell startCell, Cell endCell, NavigationDirection d)
        {
            var result = new List<string>();
            Cell c = startCell;
            while (true)
            {
                result.Add(c.Value as string);
                if (c == endCell)
                    return result;
                c = c.GetNeighbor(d);
            }
        }

        private static Func<int, object> getAnyFunction(IList<string> list)
        {
            /* foreach (StringAutoFillFunction f in StandardStringAutoFillFunctions.All)
            {
                int i = f.MatchIndex(list);
                if (i >= 0)
                {
                    f.StartOffset = i + list.Count;
                    return f.GetValue;
                }
            }*/
            foreach (List<string> l in StandardStringAutoFillFunctions.GetAll())
            {
                int matchIndex = 0;
                foreach (string s in list)
                {
                    if (!l.Exists(ss => string.Compare(s, ss, true) == 0))
                        matchIndex  = - 1;
                }
                if (matchIndex == 0)
                {
                    matchIndex = l.FindIndex(ss => string.Compare(list[0], ss, true) == 0);
                    return offset => l[(offset + matchIndex - 1 + list.Count)%l.Count];
                }
            }
            string prefix = null;
            foreach (string s in list)
            {
                string ss = s;
                int lastNumericIndex = ss.Length - 1;
                while (lastNumericIndex >= 0)
                {
                    if (Char.IsNumber(ss[lastNumericIndex]))
                        lastNumericIndex--;
                    else
                    {
                        lastNumericIndex++;
                        break;
                    }
                }
                if (lastNumericIndex == ss.Length)
                {
                    prefix = null;
                    break;
                }
                string thisPrefix = ss.Substring(0, lastNumericIndex);
                if (prefix == null)
                    prefix = thisPrefix;
                else if (thisPrefix != prefix)
                {
                    prefix = null;
                    break;
                }
            }
            if (prefix != null)
            {
                /*  var v =
                new PrefxedDoubleAutoFillFunction(prefix,
                                                         double.Parse(list[0].Substring(prefix.Length),
                                                                      CultureInfo.InvariantCulture),
                                                         double.Parse(list[list.Count - 1].Substring(prefix.Length),
                                                                      CultureInfo.InvariantCulture), list.Count);
                */
                
                //return v.GetValue;
                double firstValue = double.Parse(list[0].Substring(prefix.Length),
                                                 CultureInfo.InvariantCulture);
                                                         
                double lastValue = double.Parse(list[list.Count - 1].Substring(prefix.Length),
                                                CultureInfo.InvariantCulture);
                                                         
                double baseValue = lastValue;
                int length = list.Count;
                double increment;
                if (length > 1)
                    increment = (lastValue - firstValue) / (length - 1);
                else
                    increment = 1;
                return index => prefix + (baseValue + index * increment);
            }
            return offset => list[(offset + list.Count)%list.Count];
           
        }

    }
}